home *** CD-ROM | disk | FTP | other *** search
/ Komputer for Alle 2004 #2 / K-CD-2-2004.ISO / OpenOffice Sv / f_0397 / python-core-2.2.2 / lib / mailbox.py < prev    next >
Encoding:
Python Source  |  2003-07-18  |  9.0 KB  |  314 lines

  1. #! /usr/bin/env python
  2.  
  3. """Classes to handle Unix style, MMDF style, and MH style mailboxes."""
  4.  
  5.  
  6. import rfc822
  7. import os
  8.  
  9. __all__ = ["UnixMailbox","MmdfMailbox","MHMailbox","Maildir","BabylMailbox",
  10.            "PortableUnixMailbox"]
  11.  
  12. class _Mailbox:
  13.     def __init__(self, fp, factory=rfc822.Message):
  14.         self.fp = fp
  15.         self.seekp = 0
  16.         self.factory = factory
  17.  
  18.     def __iter__(self):
  19.         return iter(self.next, None)
  20.  
  21.     def next(self):
  22.         while 1:
  23.             self.fp.seek(self.seekp)
  24.             try:
  25.                 self._search_start()
  26.             except EOFError:
  27.                 self.seekp = self.fp.tell()
  28.                 return None
  29.             start = self.fp.tell()
  30.             self._search_end()
  31.             self.seekp = stop = self.fp.tell()
  32.             if start != stop:
  33.                 break
  34.         return self.factory(_Subfile(self.fp, start, stop))
  35.  
  36.  
  37. class _Subfile:
  38.     def __init__(self, fp, start, stop):
  39.         self.fp = fp
  40.         self.start = start
  41.         self.stop = stop
  42.         self.pos = self.start
  43.  
  44.     def read(self, length = None):
  45.         if self.pos >= self.stop:
  46.             return ''
  47.         remaining = self.stop - self.pos
  48.         if length is None or length < 0:
  49.             length = remaining
  50.         elif length > remaining:
  51.             length = remaining
  52.         self.fp.seek(self.pos)
  53.         data = self.fp.read(length)
  54.         self.pos = self.fp.tell()
  55.         return data
  56.  
  57.     def readline(self, length = None):
  58.         if self.pos >= self.stop:
  59.             return ''
  60.         if length is None:
  61.             length = self.stop - self.pos
  62.         self.fp.seek(self.pos)
  63.         data = self.fp.readline(length)
  64.         self.pos = self.fp.tell()
  65.         return data
  66.  
  67.     def readlines(self, sizehint = -1):
  68.         lines = []
  69.         while 1:
  70.             line = self.readline()
  71.             if not line:
  72.                 break
  73.             lines.append(line)
  74.             if sizehint >= 0:
  75.                 sizehint = sizehint - len(line)
  76.                 if sizehint <= 0:
  77.                     break
  78.         return lines
  79.  
  80.     def tell(self):
  81.         return self.pos - self.start
  82.  
  83.     def seek(self, pos, whence=0):
  84.         if whence == 0:
  85.             self.pos = self.start + pos
  86.         elif whence == 1:
  87.             self.pos = self.pos + pos
  88.         elif whence == 2:
  89.             self.pos = self.stop + pos
  90.  
  91.     def close(self):
  92.         del self.fp
  93.  
  94.  
  95. # Recommended to use PortableUnixMailbox instead!
  96. class UnixMailbox(_Mailbox):
  97.     def _search_start(self):
  98.         while 1:
  99.             pos = self.fp.tell()
  100.             line = self.fp.readline()
  101.             if not line:
  102.                 raise EOFError
  103.             if line[:5] == 'From ' and self._isrealfromline(line):
  104.                 self.fp.seek(pos)
  105.                 return
  106.  
  107.     def _search_end(self):
  108.         self.fp.readline()      # Throw away header line
  109.         while 1:
  110.             pos = self.fp.tell()
  111.             line = self.fp.readline()
  112.             if not line:
  113.                 return
  114.             if line[:5] == 'From ' and self._isrealfromline(line):
  115.                 self.fp.seek(pos)
  116.                 return
  117.  
  118.     # An overridable mechanism to test for From-line-ness.  You can either
  119.     # specify a different regular expression or define a whole new
  120.     # _isrealfromline() method.  Note that this only gets called for lines
  121.     # starting with the 5 characters "From ".
  122.     #
  123.     # BAW: According to
  124.     #http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html
  125.     # the only portable, reliable way to find message delimiters in a BSD (i.e
  126.     # Unix mailbox) style folder is to search for "\n\nFrom .*\n", or at the
  127.     # beginning of the file, "^From .*\n".  While _fromlinepattern below seems
  128.     # like a good idea, in practice, there are too many variations for more
  129.     # strict parsing of the line to be completely accurate.
  130.     #
  131.     # _strict_isrealfromline() is the old version which tries to do stricter
  132.     # parsing of the From_ line.  _portable_isrealfromline() simply returns
  133.     # true, since it's never called if the line doesn't already start with
  134.     # "From ".
  135.     #
  136.     # This algorithm, and the way it interacts with _search_start() and
  137.     # _search_end() may not be completely correct, because it doesn't check
  138.     # that the two characters preceding "From " are \n\n or the beginning of
  139.     # the file.  Fixing this would require a more extensive rewrite than is
  140.     # necessary.  For convenience, we've added a StrictUnixMailbox class which
  141.     # uses the older, more strict _fromlinepattern regular expression.
  142.  
  143.     _fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
  144.                        r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
  145.     _regexp = None
  146.  
  147.     def _strict_isrealfromline(self, line):
  148.         if not self._regexp:
  149.             import re
  150.             self._regexp = re.compile(self._fromlinepattern)
  151.         return self._regexp.match(line)
  152.  
  153.     def _portable_isrealfromline(self, line):
  154.         return 1
  155.  
  156.     _isrealfromline = _strict_isrealfromline
  157.  
  158.  
  159. class PortableUnixMailbox(UnixMailbox):
  160.     _isrealfromline = UnixMailbox._portable_isrealfromline
  161.  
  162.  
  163. class MmdfMailbox(_Mailbox):
  164.     def _search_start(self):
  165.         while 1:
  166.             line = self.fp.readline()
  167.             if not line:
  168.                 raise EOFError
  169.             if line[:5] == '\001\001\001\001\n':
  170.                 return
  171.  
  172.     def _search_end(self):
  173.         while 1:
  174.             pos = self.fp.tell()
  175.             line = self.fp.readline()
  176.             if not line:
  177.                 return
  178.             if line == '\001\001\001\001\n':
  179.                 self.fp.seek(pos)
  180.                 return
  181.  
  182.  
  183. class MHMailbox:
  184.     def __init__(self, dirname, factory=rfc822.Message):
  185.         import re
  186.         pat = re.compile('^[1-9][0-9]*$')
  187.         self.dirname = dirname
  188.         # the three following lines could be combined into:
  189.         # list = map(long, filter(pat.match, os.listdir(self.dirname)))
  190.         list = os.listdir(self.dirname)
  191.         list = filter(pat.match, list)
  192.         list = map(long, list)
  193.         list.sort()
  194.         # This only works in Python 1.6 or later;
  195.         # before that str() added 'L':
  196.         self.boxes = map(str, list)
  197.         self.factory = factory
  198.  
  199.     def __iter__(self):
  200.         return iter(self.next, None)
  201.  
  202.     def next(self):
  203.         if not self.boxes:
  204.             return None
  205.         fn = self.boxes[0]
  206.         del self.boxes[0]
  207.         fp = open(os.path.join(self.dirname, fn))
  208.         return self.factory(fp)
  209.  
  210.  
  211. class Maildir:
  212.     # Qmail directory mailbox
  213.  
  214.     def __init__(self, dirname, factory=rfc822.Message):
  215.         self.dirname = dirname
  216.         self.factory = factory
  217.  
  218.         # check for new mail
  219.         newdir = os.path.join(self.dirname, 'new')
  220.         boxes = [os.path.join(newdir, f)
  221.                  for f in os.listdir(newdir) if f[0] != '.']
  222.  
  223.         # Now check for current mail in this maildir
  224.         curdir = os.path.join(self.dirname, 'cur')
  225.         boxes += [os.path.join(curdir, f)
  226.                   for f in os.listdir(curdir) if f[0] != '.']
  227.  
  228.         self.boxes = boxes
  229.  
  230.     def __iter__(self):
  231.         return iter(self.next, None)
  232.  
  233.     def next(self):
  234.         if not self.boxes:
  235.             return None
  236.         fn = self.boxes[0]
  237.         del self.boxes[0]
  238.         fp = open(fn)
  239.         return self.factory(fp)
  240.  
  241.  
  242. class BabylMailbox(_Mailbox):
  243.     def _search_start(self):
  244.         while 1:
  245.             line = self.fp.readline()
  246.             if not line:
  247.                 raise EOFError
  248.             if line == '*** EOOH ***\n':
  249.                 return
  250.  
  251.     def _search_end(self):
  252.         while 1:
  253.             pos = self.fp.tell()
  254.             line = self.fp.readline()
  255.             if not line:
  256.                 return
  257.             if line == '\037\014\n':
  258.                 self.fp.seek(pos)
  259.                 return
  260.  
  261.  
  262. def _test():
  263.     import sys
  264.  
  265.     args = sys.argv[1:]
  266.     if not args:
  267.         for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
  268.             if os.environ.has_key(key):
  269.                 mbox = os.environ[key]
  270.                 break
  271.         else:
  272.             print "$MAIL, $LOGNAME nor $USER set -- who are you?"
  273.             return
  274.     else:
  275.         mbox = args[0]
  276.     if mbox[:1] == '+':
  277.         mbox = os.environ['HOME'] + '/Mail/' + mbox[1:]
  278.     elif not '/' in mbox:
  279.         mbox = '/usr/mail/' + mbox
  280.     if os.path.isdir(mbox):
  281.         if os.path.isdir(os.path.join(mbox, 'cur')):
  282.             mb = Maildir(mbox)
  283.         else:
  284.             mb = MHMailbox(mbox)
  285.     else:
  286.         fp = open(mbox, 'r')
  287.         mb = PortableUnixMailbox(fp)
  288.  
  289.     msgs = []
  290.     while 1:
  291.         msg = mb.next()
  292.         if msg is None:
  293.             break
  294.         msgs.append(msg)
  295.         if len(args) <= 1:
  296.             msg.fp = None
  297.     if len(args) > 1:
  298.         num = int(args[1])
  299.         print 'Message %d body:'%num
  300.         msg = msgs[num-1]
  301.         msg.rewindbody()
  302.         sys.stdout.write(msg.fp.read())
  303.     else:
  304.         print 'Mailbox',mbox,'has',len(msgs),'messages:'
  305.         for msg in msgs:
  306.             f = msg.getheader('from') or ""
  307.             s = msg.getheader('subject') or ""
  308.             d = msg.getheader('date') or ""
  309.             print '-%20.20s   %20.20s   %-30.30s'%(f, d[5:], s)
  310.  
  311.  
  312. if __name__ == '__main__':
  313.     _test()
  314.